#include "Application.h"
#include <ctime>
#include <SFML/Graphics.hpp>
#include <cassert>
#include "BaseState.h"
#include "LoadScreen.h"
#include "MainState.h"
#include "GameState.h"
#include "SettingsMenuState.h"
#include "PauseState.h"
#include "GameOverState.h"
#include "GraphicManager.h"
#include "Fonts.h"
#include "Utils.h"
#include "Params.h"

void Application::Init(bool fullscreen)
{
	srand((unsigned)time(NULL));

	fullscreen ? (Params::ScreenMode = Params::Fullscreen) : (Params::ScreenMode = Params::Window);

	sf::VideoMode mode;
	mode.Width = 1024;
	mode.Height = 768;
	mode.BitsPerPixel = 32;

	sf::ContextSettings settings;
	settings.DepthBits = 24;
	settings.StencilBits = 8;
	settings.AntialiasingLevel = 4;
	settings.MajorVersion = 3;
	settings.MinorVersion = 1;

	m_window = new sf::RenderWindow(mode,
		"AI Football",
		fullscreen ? sf::Style::Fullscreen : sf::Style::Close,
		settings);
	//m_window->UseVerticalSync(true);

	m_window->SetFramerateLimit(0);

	glewInit();

	m_frames = 0;
	m_lastFrameCheck = m_clock.GetElapsedTime();
	m_inputPauseTime = 0.f;

	Prepare3DViewport(0, 0, m_window->GetWidth(), m_window->GetHeight());
	
	glMatrixMode(GL_MODELVIEW);
	
	m_currentState = new LoadScreen();
	m_currentStateName = "LoadScreen";
}

void Application::Destroy()
{
	for (StateMap::iterator it = m_states.begin(); it!=m_states.end(); ++it)
	{
		delete it->second;
	}

	GraphicManager::Istance()->Destroy();
}

#pragma region SETTERS

void Application::SetState(const std::string& newState)
{
	StateMap::iterator state = m_states.find(newState);
	
	//if not find, ent == m_entMap.End()
	if(state==m_states.end())
	{
		m_currentStateName = newState;

		if (newState=="Game"){
			GameState* game = new GameState();
			game->StartGame();
			m_currentState = game;
			m_states.insert( std::pair<std::string,BaseState*>("Game",game) );
		}
		else if (newState=="SettingsMenu"){
			SettingsMenuState* settings = new SettingsMenuState();
			m_currentState = settings;
			m_states.insert( std::pair<std::string,BaseState*>("SettingsMenu", settings) );
		}
		else if (newState=="Pause"){
			PauseState* pause = new PauseState();
			m_currentState = pause;
			m_states.insert( std::pair<std::string,BaseState*>("Pause", pause) );
		}
		else if (newState=="GameOver"){
			GameOverState* gameOver = new GameOverState();
			m_currentState = gameOver;
			m_states.insert( std::pair<std::string,BaseState*>("GameOver", gameOver) );
		}
		else 
		{
			m_states.insert( std::pair<std::string,BaseState*>("Main", new MainState()) );
			m_currentStateName = "Main";
		}
	}
	else
	{
		if (newState=="Game" && m_currentStateName!="Pause")
		{
			delete state->second;
			GameState* game = new GameState();
			game->StartGame();
			state->second = game;
			m_currentState = game;
		}
		else
		{
			m_currentState = state->second;
		}
		m_currentStateName = newState;
	}
	
}

#pragma endregion // SETTERS

BaseState* Application::GetState(const std::string &stateName)
{
	StateMap::iterator state = m_states.find(stateName);
	
	//if not find, ent == m_entMap.End()
	if(state==m_states.end())
		return NULL;
	else
		return state->second;
}

void Application::Run()
{
	sf::Event event;
	while ( m_window->IsOpened())
	{
		// system events handling first
		while (m_window->GetEvent(event))
		{
			switch (event.Type)
			{
			case sf::Event::Closed:
				m_window->Close();
				break;

			case sf::Event::Resized:
				OnResize(event.Size.Width, event.Size.Height);
				break;
			}
		}

		m_window->SetActive(true);

		m_currentState->OnIdle(m_window->GetInput());

		Render();
		m_frames++;
		float fps=0.f;
		if ((fps = m_clock.GetElapsedTime() - m_lastFrameCheck)>=1.f)
		{
			fps *= m_frames;
			m_fps = " FPS: " + NumberToString(fps);
			m_frames = 0;
			m_lastFrameCheck=m_clock.GetElapsedTime();
		}
	}
}

void Application::Render()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	m_currentState->OnDraw(*m_window);

	glFlush();
	
	m_window->SaveGLStates();
	sf::Font* f = Fonts::Istance()->GetFont("Consola");
	sf::Text text(m_fps, (*f), 14);
	text.SetPosition(0, m_window->GetHeight()-25.f);
	m_window->Draw(text);
	m_window->RestoreGLStates();

	m_window->Display();
	
}

void Application::Init3D()
{
	glClearColor(0.f, 0.f, 0.f, 1.f);
	glClearDepth(1.0f);						// Depth Buffer Setup

	glEnable(GL_COLOR_MATERIAL);

	// Enable Z-buffer read and write
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glClearDepth(1.f);
	glDepthFunc(GL_LEQUAL);					// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

}

void Application::Prepare3DViewport(int left, int top, int width, int height)
{
	Init3D();

	glViewport(left, top, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	const double verticalFov = 45.0;
	const double aspectRatio = (static_cast<double>(width) / height) ;
	const double nearPlane = 1.f;
	const double farPlane = 1500.;
	gluPerspective(verticalFov,
		aspectRatio, 
		nearPlane,
		farPlane);
}

void Application::OnResize(unsigned int width, unsigned int height)
{
	Prepare3DViewport(0, 0, width, height);
}